#![allow(unused)]
//! Logic for handling a batch of inbound messages.

use std::fmt::Debug;

use openmls::{
    framing::{ProcessedMessage, ProcessedMessageContent},
    group::{MergeCommitError, MlsGroup},
};
use openmls_traits::{signatures::Signer, OpenMlsCryptoProvider};
use tracing::*;

use aspect_db_common::group::types::AppMsg;
use aspect_ident::DeviceIdent;

use super::command::Command;
use crate::infra::group_io::{GroupBroadcast, GroupOutput};

pub fn handle_command(
    group_state: &mut MlsGroup,
    cmd: Command,
    provider: &impl OpenMlsCryptoProvider,
    signer: &impl Signer,
    output: &impl GroupOutput,
    bcast: &impl GroupBroadcast,
) -> anyhow::Result<()> {
    let group_id = group_state.group_id().clone();

    match cmd {
        Command::ProcMessage(proto_msg) => {
            let pmsg = match group_state.process_message(provider, proto_msg) {
                Ok(m) => m,
                Err(e) => {
                    error!(?group_id, err = %e, "error handling message");
                    return Err(e.into());
                }
            };

            handle_processed_msg(group_state, pmsg, provider, output)?;
        }

        Command::SendMessage(mdata) => {
            let payload = mdata.application_payload();
            let msg_out = group_state.create_message(provider, signer, payload)?;
            bcast.broadcast_mls_out(msg_out)?;
        }
    }

    Ok(())
}

fn handle_processed_msg(
    group_state: &mut MlsGroup,
    pmsg: ProcessedMessage,
    provider: &impl OpenMlsCryptoProvider,
    output: &impl GroupOutput,
) -> anyhow::Result<()> {
    // TODO

    let pmsg_sender = pmsg.sender().clone();
    let pmsg_epoch = pmsg.epoch().as_u64();

    match pmsg.into_content() {
        ProcessedMessageContent::ApplicationMessage(app_msg) => {
            let payload = app_msg.into_bytes();

            let device_ident = DeviceIdent::from_hash([0; 32]);
            let out_msg = AppMsg::new(device_ident, pmsg_epoch, payload);

            output.write_msg(out_msg)?;
        }

        ProcessedMessageContent::ProposalMessage(queued_prop) => {
            // TODO checks to see if the proposal is allowed
            group_state.store_pending_proposal(*queued_prop);
        }

        ProcessedMessageContent::ExternalJoinProposalMessage(ext_join_prop) => {
            // TODO checks to see if the external join is allowed
            group_state.store_pending_proposal(*ext_join_prop);
        }

        ProcessedMessageContent::StagedCommitMessage(staged_commit) => {
            // TODO checks to see if the commit is allowed
            group_state
                .merge_staged_commit(provider, *staged_commit)
                .map_err(conv_merge_commit_error)?;
        }
    }

    Ok(())
}

fn conv_merge_commit_error<E: Debug>(err: MergeCommitError<E>) -> anyhow::Error {
    match err {
        MergeCommitError::LibraryError(e) => e.into(),
        MergeCommitError::KeyStoreError(e) => anyhow::Error::msg(format!("keystore: {e:?}")),
    }
}
